package com.lwd.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Brozen
 * @date 2019/5/27 4:20 PM
 */
public class EnhancedReflectionUtil {

    private static final Object NULL_REFERENCE = new Object();

    private static final Map<Tuple<Object, Class>, Object> annotationCache = new ConcurrentHashMap<>();

    @SuppressWarnings("unchecked")
    public static <T extends Annotation> T getAnnotation(AccessibleObject accessibleObject, Class<T> annotationClass) {
        Tuple<Object, Class> key = new Tuple<Object, Class>(accessibleObject, annotationClass);
        if (!annotationCache.containsKey(key)) {
            T annotation = accessibleObject.getAnnotation(annotationClass);
            annotationCache.put(key, annotation == null ? NULL_REFERENCE : annotation);
            return annotation;
        } else {
            Object annotationObj = annotationCache.get(key);
            if (annotationObj == null || annotationObj == NULL_REFERENCE || !(annotationObj instanceof Annotation)) {
                return null;
            } else {
                return (T) annotationObj;
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static <T extends Annotation> T getAnnotation(Class clazz, Class<T> annotationClass) {
        Tuple<Object, Class> key = new Tuple<Object, Class>(clazz, annotationClass);
        if (!annotationCache.containsKey(key)) {
            T annotation = (T) clazz.getAnnotation(annotationClass);
            annotationCache.put(key, annotation == null ? NULL_REFERENCE : annotation);
            return annotation;
        } else {
            Object annotationObj = annotationCache.get(key);
            if (annotationObj == null || annotationObj == NULL_REFERENCE || !(annotationObj instanceof Annotation)) {
                return null;
            } else {
                return (T) annotationObj;
            }
        }
    }

}
